/*
* Copyright 2015-2016 http://hsweb.me
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hsweb.web.datasource.dynamic;
import com.atomikos.icatch.config.UserTransactionService;
import com.atomikos.icatch.config.UserTransactionServiceImp;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.hsweb.commons.ClassUtils;
import org.hsweb.commons.StringUtils;
import org.hsweb.web.core.datasource.DataSourceHolder;
import org.hsweb.web.core.datasource.DynamicDataSource;
import org.hsweb.web.core.utils.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.atomikos.AtomikosProperties;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.transaction.jta.JtaTransactionManager;
import javax.sql.DataSource;
import javax.transaction.SystemException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Properties;
@Configuration
@ConditionalOnMissingBean(DynamicDataSource.class)
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
@ComponentScan("org.hsweb.web.datasource.dynamic")
public class DynamicDataSourceAutoConfiguration {
@Autowired
private DynamicDataSourceProperties properties;
@Bean(initMethod = "init", destroyMethod = "shutdownForce")
public UserTransactionServiceImp userTransactionService() {
AtomikosProperties atomikosProperties = properties.getIcatch();
Properties properties = new Properties();
properties.putAll(atomikosProperties.asProperties());
if (StringUtils.isNullOrEmpty(properties.get("com.atomikos.icatch.service"))) {
properties.put("com.atomikos.icatch.service", "com.atomikos.icatch.standalone.UserTransactionServiceFactory");
}
return new UserTransactionServiceImp(properties);
}
/**
* 默认数据库链接
*/
@Primary
@Bean(initMethod = "init", name = "dataSource", destroyMethod = "close")
@ConditionalOnMissingBean(DataSource.class)
@Cacheable
public DataSource dataSource() {
AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();
properties.putProperties(dataSourceBean);
return dataSourceBean;
}
@Bean(name = "dynamicDataSource")
public DynamicXaDataSourceImpl dynamicXaDataSource(@Qualifier("dataSource") DataSource dataSource) {
DynamicXaDataSourceImpl dynamicXaDataSource = new DynamicXaDataSourceImpl(dataSource, properties.getType());
DataSourceHolder.install(dynamicXaDataSource);
return dynamicXaDataSource;
}
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager userTransactionManager(
UserTransactionService userTransactionService) {
UserTransactionManager transactionManager = new UserTransactionManager();
transactionManager.setForceShutdown(true);
transactionManager.setStartupTransactionService(false);
return transactionManager;
}
@Bean
public UserTransactionImp userTransaction() throws SystemException {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(properties.getTransactionTimeout());
return userTransactionImp;
}
@Bean
public JtaTransactionManager transactionManager(UserTransactionManager userTransactionManager, UserTransactionImp userTransaction) throws SystemException {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setTransactionManager(userTransactionManager);
jtaTransactionManager.setUserTransaction(userTransaction);
jtaTransactionManager.setAllowCustomIsolationLevels(true);
return jtaTransactionManager;
}
@Bean(name = "sqlExecutor")
@ConditionalOnMissingBean(DynamicDataSourceSqlExecutorService.class)
public DynamicDataSourceSqlExecutorService sqlExecutor() {
return new DynamicDataSourceSqlExecutorService();
}
@Bean
public AnnotationDataSourceChangeConfiguration annotationDataSourceChangeConfiguration() {
return new AnnotationDataSourceChangeConfiguration();
}
@Aspect
public static class AnnotationDataSourceChangeConfiguration {
private <T extends Annotation> T getAnn(ProceedingJoinPoint pjp, Class<T> annClass) {
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method m = signature.getMethod();
T a = AnnotationUtils.findAnnotation(m, annClass);
if (a != null) return a;
Class<?> targetClass = pjp.getTarget().getClass();
m = org.springframework.util.ClassUtils.getMostSpecificMethod(m, targetClass);
a = AnnotationUtils.findAnnotation(m, annClass);
if (a != null) return a;
return AnnotationUtils.findAnnotation(pjp.getTarget().getClass(), annClass);
}
@Around(value = "within(@org.hsweb.web.datasource.dynamic.UseDataSource *)||@annotation(org.hsweb.web.datasource.dynamic.UseDataSource)")
public Object useDatasource(ProceedingJoinPoint pjp) throws Throwable {
UseDataSource ann = getAnn(pjp, UseDataSource.class);
try {
if (null != ann) {
DynamicDataSource.use(ann.value());
}
return pjp.proceed();
} finally {
if (null != ann) {
DynamicDataSource.useDefault(false);
}
}
}
@Around(value = "within(@org.hsweb.web.datasource.dynamic.UseDefaultDataSource *)||@annotation(org.hsweb.web.datasource.dynamic.UseDataSource)")
public Object useDefaultDatasource(ProceedingJoinPoint pjp) throws Throwable {
UseDefaultDataSource ann = getAnn(pjp, UseDefaultDataSource.class);
try {
if (null != ann) {
DynamicDataSource.useDefault(ann.value());
}
return pjp.proceed();
} finally {
if (ann != null && ann.value())
DynamicDataSource.useLast();
}
}
}
}